#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "../include/platform.h"
#include "../include/common.h"
#include <time.h>
#include <sys/stat.h>
#include <pthread.h>

// Simple file-based storage. CSV for users, binary for goods.
static int next_user_id = 1;
static size_t next_goods_id = 1;
// finer-grained locks
static pthread_mutex_t users_mutex = PTHREAD_MUTEX_INITIALIZER;        // protects USERLIST and next_user_id
static pthread_rwlock_t goods_rwlock = PTHREAD_RWLOCK_INITIALIZER;     // protects GOODSLIST and next_goods_id (rwlock)
static pthread_mutex_t tradelog_mutex = PTHREAD_MUTEX_INITIALIZER;     // protects TRADELOG


int platform_init(void) {
    // try to read users to set next_user_id
    FILE *f = fopen(USERLIST, "r");
    if (f) {
        char line[256];
        int maxid = 0;
        while (fgets(line, sizeof(line), f)) {
            int id = 0;
            if (sscanf(line, "%d,", &id) == 1) {
                if (id > maxid) maxid = id;
            }
        }
        next_user_id = maxid + 1;
        fclose(f);
    }

    // read goods file to set next_goods_id
    FILE *g = fopen(GOODSLIST, "rb");
    if (g) {
        Goods tmp;
        size_t maxid = 0;
        while (fread(&tmp, sizeof(Goods), 1, g) == 1) {
            if (tmp.id > maxid) maxid = tmp.id;
        }
        next_goods_id = maxid + 1;
        fclose(g);
    }
    return 0;
}

void platform_close(void) {
    // destroy all locks
    pthread_mutex_destroy(&users_mutex);
    pthread_rwlock_destroy(&goods_rwlock);
    pthread_mutex_destroy(&tradelog_mutex);
}

int platform_add_user(const char *name) {
    int id = -1;
    pthread_mutex_lock(&users_mutex);
    FILE *f = fopen(USERLIST, "a+");
    if (!f) {
        pthread_mutex_unlock(&users_mutex);
        return -1;
    }
    id = next_user_id++;
    fprintf(f, "%d,%s,0,0\n", id, name);
    fclose(f);
    pthread_mutex_unlock(&users_mutex);
    return id;
}

// helper: lookup user name by id (reads USERLIST)
static void get_username_by_id(int id, char *out, size_t len) {
    out[0] = '\0';
    pthread_mutex_lock(&users_mutex);
    FILE *f = fopen(USERLIST, "r");
    if (!f) {
        pthread_mutex_unlock(&users_mutex);
        return;
    }
    char line[256];
    while (fgets(line, sizeof(line), f)) {
        int uid = 0; char name[NAME_SIZE];
        if (sscanf(line, "%d,%63[^,]", &uid, name) >= 1) {
            if (uid == id) {
                strncpy(out, name, len-1);
                out[len-1] = '\0';
                break;
            }
        }
    }
    fclose(f);
    pthread_mutex_unlock(&users_mutex);
}

int platform_add_goods(const Goods *g) {
    int gid = -1;
    pthread_rwlock_wrlock(&goods_rwlock);
    FILE *f = fopen(GOODSLIST, "ab");
    if (!f) {
        pthread_rwlock_unlock(&goods_rwlock);
        return -1;
    }
    Goods tmp = *g;
    tmp.id = next_goods_id++;
    fwrite(&tmp, sizeof(Goods), 1, f);
    fclose(f);
    gid = (int)tmp.id;
    pthread_rwlock_unlock(&goods_rwlock);
    return gid;
}

static int _load_goods_by_id_nolock(int id, Goods *out) {

    FILE *f = fopen(GOODSLIST, "rb+");
    if (!f) return -1;
    Goods tmp;
    while (fread(&tmp, sizeof(Goods), 1, f) == 1) {
        if (tmp.id == id) {
            if (out) *out = tmp;
            fclose(f);
            return 0;
        }
    }
    fclose(f);
    return -1;
}

// helper to find goods by id
static int load_goods_by_id(int id, Goods *out) {
    int ret;
    pthread_rwlock_rdlock(&goods_rwlock);
    ret = _load_goods_by_id_nolock(id, out);
    pthread_rwlock_unlock(&goods_rwlock);
    return ret;
}

static int _delete_goods_nolock(int id) {
    FILE *f = fopen(GOODSLIST, "rb+");
    if (!f) return -1;

    fseek(f, 0, SEEK_END);
    long file_size = ftell(f);
    fseek(f, 0, SEEK_SET);

    if (file_size == 0) {
        fclose(f);
        return -1;
    }
    
    size_t goods_count = file_size / sizeof(Goods);

    if (goods_count == 1) {
        Goods single;
        if (fread(&single, sizeof(Goods), 1, f) == 1 && single.id == id) {
            fclose(f);
            // 清空文件
            f = fopen(GOODSLIST, "wb");
            if (f) fclose(f);
            return 0;
        }
        fclose(f);
        return -1;
    }

    fseek(f, 0, SEEK_SET);
    Goods item, last_item;
    long target_pos = -1;

    fseek(f, -((long)sizeof(Goods)), SEEK_END);
    if (fread(&last_item, sizeof(Goods), 1, f) != 1) {
        fclose(f);
        return -1;
    }

    fseek(f, 0, SEEK_SET);
    while (ftell(f) < file_size) {
        long current_pos = ftell(f);
        if (fread(&item, sizeof(Goods), 1, f) != 1) {
            break;
        }
        if (item.id == id) {
            target_pos = current_pos;
            break;
        }
    }
    
    if (target_pos == -1) {
        fclose(f); 
        return -1;
    }

    last_item.id = item.id; // 将删除的商品id赋值给最后一个商品，保持id唯一性

    fseek(f, target_pos, SEEK_SET);
    if (fwrite(&last_item, sizeof(Goods), 1, f) != 1) {
        fclose(f);
        return -1;
    }

    if (ftruncate(fileno(f), file_size - sizeof(Goods)) != 0) {
        fclose(f);
        return -1;
    }
    
    next_goods_id--; // 调整下一个商品ID
    fclose(f);
    return 0;
}

// update goods record (overwrite this id)
static int _save_goods_nolock(const Goods *g) {
    FILE *f = fopen(GOODSLIST, "rb+");
    if (!f) return -1;
    Goods tmp;
    while (fread(&tmp, sizeof(Goods), 1, f) == 1) {
        if (tmp.id == g->id) {
            fseek(f, -((long)sizeof(Goods)), SEEK_CUR);
            fwrite(g, sizeof(Goods), 1, f);
            fclose(f);
            return 0;
        }
    }
    fclose(f);
    return -1;
}

// list goods: fill reply->data with lines "id,name\n"
static void list_goods(Reply *reply) {
    reply->err = SUCCESS;
    reply->type = LIST;
    reply->user_id = 0;
    reply->total_price = 0;
    reply->data[0] = '\0';

    pthread_rwlock_rdlock(&goods_rwlock);
    FILE *f = fopen(GOODSLIST, "rb");
    if (!f) {
        pthread_rwlock_unlock(&goods_rwlock);
        return;
    }
    Goods tmp;
    char buf[128];
    while (fread(&tmp, sizeof(Goods), 1, f) == 1) {
        char ownername[NAME_SIZE] = {0};
        get_username_by_id(tmp.owner_id, ownername, sizeof(ownername));
        snprintf(buf, sizeof(buf), "%zu,%s,%s,%d\n", tmp.id, tmp.name, ownername, tmp.storage);
        strncat(reply->data, buf, MAX_DATA - strlen(reply->data) - 1);
    }
    fclose(f);
    pthread_rwlock_unlock(&goods_rwlock);
}

Reply execute_cmd(const Command *cmd) {
    Reply r;
    memset(&r, 0, sizeof(r));
    r.err = SUCCESS;
    r.type = cmd->type;

    if (cmd->user_id == -1 && cmd->type != LOGIN) {
        r.err = ERRNOTLOGGEDIN;
        return r;
    }

    if (cmd->type == QUIT) {
        return r;
    }

    if (cmd->type == LOGIN) {
        // if user exists, return its id; else add
        FILE *f = fopen(USERLIST, "r");
        if (f) {
            char line[256];
            while (fgets(line, sizeof(line), f)) {
                int id = 0; char name[NAME_SIZE];
                /* read id and name (name up to next comma) */
                if (sscanf(line, "%d,%63[^,]", &id, name) >= 1) {
                    if (strcmp(name, cmd->name) == 0) {
                        r.user_id = id;
                        fclose(f);
                        return r;
                    }
                }
            }
            fclose(f);
        }
        int id = platform_add_user(cmd->name);
        r.user_id = id;
        return r;
    }

    if (cmd->type == SELL) {
        Goods g;
        memset(&g, 0, sizeof(g));
        g.price = cmd->price; // use number as price when selling
        g.storage = cmd->number;
        g.owner_id = cmd->user_id;
        strncpy(g.name, cmd->name, NAME_SIZE-1);
        strncpy(g.description, cmd->description, DESCRIPTION_SIZE-1);
        int gid = platform_add_goods(&g);
        r.user_id = gid;
        // prepare a friendly message describing the listing
        {
            char ownername[NAME_SIZE] = {0};
            if (g.owner_id > 0) get_username_by_id(g.owner_id, ownername, sizeof(ownername));
            snprintf(r.data, sizeof(r.data), "Listed goods id=%d name=\"%s\" price=%d owner=%s(id=%d)", gid, g.name, g.price, ownername[0]?ownername:"<unknown>", g.owner_id);
        }
        return r;
    }

    if (cmd->type == LIST) {
        list_goods(&r);
        return r;
    }

    if (cmd->type == VIEW) {
        Goods g;
        if (load_goods_by_id(cmd->good_id, &g) == 0) {
            strncpy(r.data, g.description, MAX_DATA-1);
            r.err = SUCCESS;
        } else {
            r.err = ERRNOTFOUND;
        }
        return r;
    }

    if (cmd->type == BUY) {
        Goods g;
        // perform atomic load-modify-save under goods write lock

        pthread_rwlock_wrlock(&goods_rwlock);

        if (_load_goods_by_id_nolock(cmd->good_id, &g) != 0) {
            pthread_rwlock_unlock(&goods_rwlock);
            r.err = ERRNOTFOUND;
            return r;
        }

        if (g.storage < cmd->number) {
            pthread_rwlock_unlock(&goods_rwlock);
            r.err = ERRZERO;
            return r;
        }
        g.storage -= cmd->number;

        if (g.storage == 0) {
            if (_delete_goods_nolock(g.id) != 0) {
                pthread_rwlock_unlock(&goods_rwlock);
                r.err = ERRNOTFOUND;
                return r;
            }
        }
        else {
            if (_save_goods_nolock(&g) != 0) {
                pthread_rwlock_unlock(&goods_rwlock);
                r.err = ERRNOTFOUND;
                return r;
            }
        }
        pthread_rwlock_unlock(&goods_rwlock);
        r.total_price = g.price * cmd->number;

        char buyername[NAME_SIZE] = {0};
        char sellername[NAME_SIZE] = {0};

        // append trade record to TRADELOG (with header and names)
        {
            // ensure file exists and has header
            pthread_mutex_lock(&tradelog_mutex);
            FILE *f = fopen(TRADELOG, "a+");
            if (f) {
                // check if file is empty -> write header
                struct stat st;
                if (fstat(fileno(f), &st) == 0) {
                    if (st.st_size == 0) {
                        fprintf(f, "timestamp,buyer_id,buyer_name,seller_id,seller_name,goods_id,goods_name,number,total_price\n");
                    }
                }
                time_t t = time(NULL);
                struct tm tm = *localtime(&t);
                
                get_username_by_id(cmd->user_id, buyername, sizeof(buyername));
                get_username_by_id(g.owner_id, sellername, sizeof(sellername));

        // write CSV line with names
        // fields: timestamp,buyer_id,buyer_name,seller_id,seller_name,goods_id,goods_name,number,total_price
        fprintf(f, "%04d-%02d-%02d %02d:%02d:%02d,%d,%s,%d,%s,%zu,%s,%d,%d\n",
            tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
            tm.tm_hour, tm.tm_min, tm.tm_sec,
            cmd->user_id, buyername[0]?buyername:"<unknown>",
            g.owner_id, sellername[0]?sellername:"<unknown>",
            g.id, g.name, cmd->number, r.total_price);
                fclose(f);
            }
            pthread_mutex_unlock(&tradelog_mutex);
        }
        // prepare a friendly reply message
        snprintf(r.data, sizeof(r.data), "Trade success: buyer=%s(id=%d) seller=%s(id=%d) goods=\"%s\"(id=%zu) number=%d total=%d",
                 buyername[0]?buyername:"<unknown>", cmd->user_id,
                 sellername[0]?sellername:"<unknown>", g.owner_id,
                 g.name, g.id, cmd->number, r.total_price);
    
        return r;
    }

    if (cmd->type == RECORDS) {
        // read TRADELOG and collect lines where buyer_id==user or seller_id==user
        FILE *f = fopen(TRADELOG, "r");
        if (!f) {
            r.err = SUCCESS;
            r.data[0] = '\0';
            return r;
        }
        char line[1024];
        int sum = 0;
        r.data[0] = '\0';
        while (fgets(line, sizeof(line), f)) {
            // skip header
            if (strncmp(line, "timestamp", 9) == 0) continue;
            // parse CSV: timestamp,buyer_id,buyer_name,seller_id,seller_name,goods_id,goods_name,number,total_price
            char ts[64] = {0};
            int buyer=0, seller=0, number=0, total=0;
            size_t goodsid = 0;
            char buyername[NAME_SIZE] = {0}, sellername[NAME_SIZE] = {0}, goodsname[NAME_SIZE] = {0};
            int scanned = sscanf(line, "%63[^,],%d,%63[^,],%d,%63[^,],%zu,%63[^,],%d,%d",
                                 ts, &buyer, buyername, &seller, sellername, &goodsid, goodsname, &number, &total);
            if (scanned >= 2) {
                if (buyer == cmd->user_id || seller == cmd->user_id) {
                    if (buyer == cmd->user_id) sum -= total;
                    if (seller == cmd->user_id) sum += total;
      
                    char buf[256];
                    snprintf(buf, sizeof(buf), "Timestamp:%s Buyer:%s(id=%d) Seller:%s(id=%d) Goods:%s(id=%zu) Number:%d Total:%d\n",
                             ts, buyername[0]?buyername:"<unknown>", buyer,
                             sellername[0]?sellername:"<unknown>", seller,
                             goodsname[0]?goodsname:"<unknown>", goodsid,
                             number, total);
                    if (strlen(r.data) + strlen(buf) + 1 < sizeof(r.data)) {
                        strncat(r.data, buf, sizeof(r.data) - strlen(r.data) - 1);
                    }
                }
            }
        }
        char summary[128];
        snprintf(summary, sizeof(summary), "Summary: %d\n", sum);
        if (strlen(r.data) + strlen(summary) + 1 < sizeof(r.data)) {
            strncat(r.data, summary, sizeof(r.data) - strlen(r.data) - 1);
        }
        fclose(f);
        return r;
    }

    r.err = SUCCESS;
    return r;
}
